/*
 * Scene.h
 *
 *  Created on: May 14, 2012
 *      Author: eliana
 */

#ifndef SCENE_H_
#define SCENE_H_

#include <vector>
#include "Object.h"
#include "Camera.h"
#include "Light.h"
#include "Material.h"
#include "Color.h"
#include "Grid.h"

class Scene
{
    public:
        
        /**
         * Types of space subdividion implemented
         */
        enum SpaceSubdivisionType
        {
            REGULAR_GRID,
            KD_TREE,
            NONE
        };
        
        /**
         * Constructor
         */
        Scene();
        
        /**
         * Destructor
         */
        virtual ~Scene();
        
        /**
         * Sets the parameters of soft shadow.
         * The soft shadow is generated by spawning multiples lights for each 
         * scene light. These virtual light sources are generated inside a 
         * circular area around the scene light
         * @param nSoftLights Number of generated "virtual" light sources for 
         *                    each "real" one
         * @param softShadowRadius Radius of the area in which the virtual light 
         *                         sources can spawn
         */
        void setSoftShadow( int nSoftLights, float softShadowRadius );

        /**
         * @param camera Camera used to render the scene
         */
        void setCamera( Camera* camera );

        /**
         * @param ambientLight Color of the ambient light
         */
        void setAmbientLight( Color ambientLight );

        /**
         * @param backgroungColor Color of the background
         */
        void setBackgroundColor( Color backgroungColor );

        /**
         * @param light Light to be added
         */
        void addLight( Light* light );

        /**
         * @param object Object to be added
         */
        void addObject( Object* object );

        /**
         * Renders the scene into an image
         * @return Resulting image
         */
        Image* render();

        /**
         * @param material New material added to the scene's collection
         */
        void addMaterial( Material* material );

        /**
         * @param index Index of the wanted material
         * @return Material object
         */
        Material* getMaterial( int index );               

        /**
         * Indicates that the scene is no longer being built
         */
        void finishBuildingScene();
        
        /**
         * Sets the space subdivision used to render the scene
         * @param type Space subdivision type
         */
        void setSpaceSubdivisionType( SpaceSubdivisionType type );
        
        /**
         * Rebuilds the grid of with regular cells
         * @param nX Number of cells in the X direction
         * @param nY Number of cells in the Y direction
         * @param nZ Number of cells in the Z direction
         */
        void rebuildGrid( int nX, int nY, int nZ );
        
        /**
         * Turns ambient light on or of
         * @param isActive true if ON false otherwise
         */
        void toggleAmbient( bool isActive );
        
         /**
         * Turns diffuse light on or of
         * @param isActive true if ON false otherwise
         */
        void toggleDiffuse( bool isActive );
        
         /**
         * Turns specular light on or of
         * @param isActive true if ON false otherwise
         */
        void toggleSpecular( bool isActive );
        
         /**
         * Turns shadows on or of
         * @param isActive true if ON false otherwise
         */
        void toggleShadow( bool isActive );
        
         /**
         * Turns soft shadows on or of
         * @param isActive true if ON false otherwise
         */
        void toggleSoftShadow( bool isActive );
        
         /**
         * Turns reflections on or of
         * @param isActive true if ON false otherwise
         */
        void toggleReflection( bool isActive );
        
         /**
         * Turns anti aliasing on or of
         * @param isActive true if ON false otherwise
         */
        void toggleAntiAlias( bool isActive );
        
        /**
         * Sets the memory size of each ray
         * @param size Size of the memory
         */
       void setRayMemorySize( int size );
       
       /**
        * @return Reference to the scene's camera
        */
       Camera* getCamera();
       
        /**
        * Builds the bounding box of the grid from the objects of the scene
        * @param objects
        */
       void buildBoundingBox();
       
       /**
        * @return Scene's bounding box
        */
       Box* getBoundingBox();
       
    private:

        /**
         * Given a Ray, computes the position, normal vector and the object Id 
         * of the nearest intersection
         * @param ray Ray
         * @param point Position of the nearest intersection
         * @param normal Normal vector of the nearest intersection
         * @param objectID Object Id of the nearest intersection
         * @return true if and intersection occurred, false otherwise
         */
        bool computeNearestRayIntersection( Ray& ray, Vector4D& point, Vector4D& normal, int& objectID );
        
        bool computeNearestRayIntersectionBruteForce( Ray& ray, Vector4D& point, Vector4D& normal, int& objectID );
        
        bool computeNearestRayIntersectionRegularGrid( Ray& ray, Vector4D& point, Vector4D& normal, int& objectID );
        
        bool computeCellNearestRayIntersection( Ray& ray, Vector4D& point, Vector4D& normal, int& objectID, std::vector<int>* objects );
        
        
        /**
         * Given a Ray, computes the color of the pixel
         * @param ray Ray
         * @param colorOut Computed color
         * @param depth Number of the current bounce in the reflection algorithm.
         */
        void computeRayColor( Ray& ray, Color& colorOut, int depth );
        
        /**
         * Gives the shaded color of object seen by the ray
         * @param ray Ray that hits the object
         * @param objectID Id of the hit object
         * @param normal Normal vector of the objects surface in the point of intersection
         * @param point Point of intersection
         * @param colorOut shaded color seen by the ray
         */
        void shade( Ray& ray, int objectID, Vector4D& normal, Vector4D& point, Color& colorOut );
        
        /**
         * Return how "in shadow" is the point of the object
         * @param point Point in the object's surface
         * @param lightID Id of the light casting shadow
         * @param objectID Id of the object
         * @return The shadow factor where 0.0 means no light, and 1.0 full light
         */
        double computeShadowFactor( Vector4D& point, int lightID, int objectID );
        
        /**
         * Tells if the a point if in shadow of a given light source
         * @param point Point in the object's surface
         * @param light Light source
         * @param objectID Id of the object
         * @return true if in shadow, false otherwise
         */
        bool inShadow( Vector4D& point, Light* light, int objectID );
        
        /**
         * Adds the Lambertian component to the given color
         * @param objectID Id of the object being colored
         * @param lightID Id of the light source
         * @param normal Normal vector in the surface of the object
         * @param point Point of intersection of the Ray
         * @param colorOut Color of the object
         * @param shadowFactor Shadow factor
         */
        void addLambertianComponent( int objectID, int lightID, Vector4D& normal, Vector4D& point, Color& colorOut, double shadowFactor );

        /**
         * Adds the Specular component to the given color
         * @param objectID Id of the object being colored
         * @param lightID Id of the light source
         * @param normal Normal vector in the surface of the object
         * @param point Point of intersection of the Ray
         * @param colorOut Color of the object
         * @param shadowFactor Shadow factor
         */
        void addSpecularComponent( Ray& ray, int objectID, int lightID, Vector4D& normal, Vector4D& point, Color& colorOut, double shadowFactor );
        
        /**
         * Adds the ambient component to the given color
         * @param objectID Id of the object
         * @param colorOut Color of the object
         * @param point Point of intersection of the Ray
         */
        void addAmbienteComponent( int objectID, Color& colorOut, Vector4D& point );
        
        /**
         * Adds the Reflective component to the given color
         * @param objectID Id of the object being colored
         * @param lightID Id of the light source
         * @param normal Normal vector in the surface of the object
         * @param point Point of intersection of the Ray
         * @param colorOut Color of the object
         * @param depth Current depth of the reflection recursive method
         */
        void addReflectionComponent( int objectID, Ray& ray, Vector4D& normal, Vector4D& point, Color& colorOut, int depth );       
        
        /** Array of objects in the scene */
        std::vector<Object*> _objects;
        
        /** Array of light sources */
        std::vector<Light*> _lights;
        
        /** Array of "virtual" light sources spawned by the soft light algorithm */
        std::vector<Light*> _softShadowLights;
        
        /** Array of materials */
        std::vector<Material*> _materials;
        
        /** Camera */
        Camera* _camera;

        /** Color of the ambient light */
        Color _ambientLight;
        
        /** Background color */
        Color _backgroundColor;
        
        /** Effects toggles */
        bool _ambient;
        bool _diffuse;
        bool _specular;
        bool _shadow;
        bool _reflection;
        bool _softShadow;
        bool _antiAlias;

        /** Radius of the area in which the virtual light sources can spawn */
        float _softShadowRadius;
        
        /** Number of generated "virtual" light sources for each "real" one */
        int _nSoftShadowLights;
        
        /** Space subdivision type used to render the scene */
        SpaceSubdivisionType _spaceSubdivisionType;
        
        /** Grid used to render the scene */
        Grid* _grid;
        
        /** Number of objects the ray can store in it's memory */
        int _rayMemorySize;
        
        /** Scene's bounding box */
        Box* _boundingBox;
};

#endif /* SCENE_H_ */
